﻿#include "db_api_.h"

static int add_record(int id, FOLDERRECORD* rec)
{
	int res = 0;
	unsigned int br;
	res += f_lseek(&hDB->fdb, sizeof(FOLDERRECORD)*(long long)id);
	res += f_write(&hDB->fdb, rec, sizeof(FOLDERRECORD), &br);
	return res;
}

static int scan_file(char* root)
{
	int res = 0;
	DIR dj;         /* Directory object */
	FILINFO fno;    /* File information */
	FOLDERRECORD rec = { 0 };
	int id = 1;

	res += f_findfirst(&dj, &fno, root, "*");	if (res)return res;

	while (res == FR_OK && fno.fname[0])
	{
#if SORT == 1
		if (id >= MAXRECORD) break;
#endif
		if (((fno.fattrib & (AM_DIR | AM_HID | AM_SYS)) == AM_DIR)
			&& (0 != strcmp(fno.fname, "."))
			&& (0 != strcmp(fno.fname, "..")))
		{
			char path[MAXPATH];
			int tmp_len = strlen(root) + strlen("\\") + strlen(fno.fname);
   			if (tmp_len < MAXPATH)
			{
				strcpy(path, root);
				strcat(path, "\\");
				strcat(path, fno.fname);
				if(dircmp(path, WAVEROOT) == 0 || is_ignore_dir(path) == 0)
				{
					strcpy(rec.path, path);
					dir_to_utf8((const unsigned char*)fno.fname, (unsigned char*)rec.fname, MAXUTF8NAME);
					rec.media_type = MEDIA_HI_FOLDER;
					res += add_record(id++, &rec);
				}
			}
		}
		else if ((fno.fattrib & (AM_ARC | AM_DIR | AM_HID | AM_SYS)) == AM_ARC)
		{
			int media_type;
			char path[MAXPATH];
			int tmp_len = strlen(root) + strlen("\\") + strlen(fno.fname);
   			if (tmp_len < MAXPATH)
			{
				strcpy(path, root);
				strcat(path, "\\");
				strcat(path, fno.fname);
				media_type = file_type(path);
	
				switch(media_type & 0xffff0000)
				{
				case MEDIA_HI_AUDIO:
				case MEDIA_HI_VIDEO:
				case MEDIA_HI_IMAGE:
				case MEDIA_HI_EBOOK:
				case MEDIA_HI_WAVE:
				case MEDIA_HI_FOLDER:
					strcpy(rec.path, path);
					name_to_utf8((const unsigned char*)fno.fname, (unsigned char*)rec.fname, MAXUTF8NAME);
					rec.media_type = media_type;
					res += add_record(id++, &rec);
					break;
	
				default:
					break;
				}
			}
		}

		res += f_findnext(&dj, &fno);				if (res)break;
		DB_SLP
	}

	res += f_closedir(&dj);
	return res;
}

static int get_dir_depth(const char* dir)
{
	int i;
	int start, end;
	int ret = 0;

	assert(dir);
	start = strlen(SDROOT);
	end = strlen(dir)-1;

	for (i = start; i < end; i++)
	{
		if (dir[i] == '/' || dir[i] == '\\')
			ret++;
	}

	return ret;
}

static char* get_folder_db_name(const char* dir)
{
	int depth = get_dir_depth(dir);
	switch (depth)
	{
	case 0:	return F_DBFOLDER0;
	case 1:	return F_DBFOLDER1;
	case 2:	return F_DBFOLDER2;
	case 3:	return F_DBFOLDER3;
	case 4:	return F_DBFOLDER4;
	case 5:	return F_DBFOLDER5;
	case 6:	return F_DBFOLDER6;
	case 7:	return F_DBFOLDER7;
	default:return 0;
	}
}

int need_scan(const char* dir, const char* f_folder)
{
	int res = 0;
	unsigned int br;
	FOLDERRECORD rec = { 0 };

	res = f_open(&hDB->fdb, f_folder, (unsigned char)(FA_READ));
	if (res == FR_OK)
	{
		res = f_read(&hDB->fdb, &rec, sizeof(FOLDERRECORD), &br);
		if (res == FR_OK && br == sizeof(FOLDERRECORD))
		{
			if (dircmp(rec.path, dir) == 0)
			{
				f_close(&hDB->fdb);
				return 0;
			}
		}
	}
	
	f_close(&hDB->fdb);
	return 1;
}

#if SORT == 1
static void get_sortid(FOLDERRECORD* rec, unsigned long long id[2])
{
	int i;
	int len;
	char name[MAXPATH] = {0};
	
	split_path(rec->path, NULL, NULL, name, NULL);
	len = strlen(name);
	id[0] = id[1] = 0;

	if(rec->media_type & MEDIA_HI_FOLDER)
	{
		id[0] = 0x0000000000000000;
	}
	else
	{
		id[0] = 0x1000000000000000;
	}

	for(i=0; i<7 && i<len; i++)
	{
		unsigned long long c = (unsigned long long)(unsigned char)(name[i]);
		id[0] |= c<<((6-i)*8);
	}
	for(i=7; i<15 && i<len; i++)
	{
		unsigned long long c = (unsigned long long)(unsigned char)(name[i]);
		id[1] |= c<<((14-i)*8);
	}
}

static int load_sort(const char* f_folder, SORTNODE *tmp, unsigned int count)
{
	unsigned int i;
	FIL f;
	int res = 0;
	unsigned int br;
	FOLDERRECORD rec;

	res += f_open(&f, f_folder, (unsigned char)(FA_READ));
	if(res) return -1;

	res += f_lseek(&f, sizeof(FOLDERRECORD));

	for (i = 0; i < count; i++)
	{
		tmp[i].offset = (int)f_tell(&f);
		res += f_read(&f, &rec, sizeof(FOLDERRECORD), &br);
		if (FR_OK == res && sizeof(FOLDERRECORD) == br)
		{
			get_sortid(&rec, tmp[i].id);
		}
	}

	res += f_close(&f);
	return res;
}

static int save_sort(const char* f_folder, SORTNODE *tmp, unsigned int count)
{
	unsigned int i;
	int res = 0;
	unsigned int br;
	FOLDERRECORD rec;
	FIL f_src, f_des;

	res += f_open(&f_src, f_folder, (unsigned char)(FA_READ));
	if(res) return -1;
	res += f_open(&f_des, (const char*)(F_SORT), (unsigned char)(FA_READ | FA_WRITE | FA_CREATE_ALWAYS));
	if(res) return -1;

	res += f_read(&f_src, &rec, sizeof(FOLDERRECORD), &br);
	res += f_write(&f_des, &rec, sizeof(FOLDERRECORD), &br);

	for(i=0; i<count; i++)
	{
		res += f_lseek(&f_src, tmp[i].offset);
		res += f_read(&f_src, &rec, sizeof(FOLDERRECORD), &br);
		res += f_write(&f_des, &rec, sizeof(FOLDERRECORD), &br);
	}

	res += f_close(&f_src);
	res += f_close(&f_des);

	res += f_unlink(f_folder);
	res += f_rename((const char*)(F_SORT), f_folder);

	return res;
}

static int sort(const char* f_folder)
{
	int res = 0;
	unsigned int count;
	SORTNODE *tmp = 0;

	res += f_open(&hDB->fdb, f_folder, (unsigned char)(FA_READ));
	if (res)	return -1;
	count = (unsigned int)f_size(&hDB->fdb) / sizeof(FOLDERRECORD) - 1;
	res += f_close(&hDB->fdb);

	assert(MAXRECORD >= count);
	if(count < 2) return 0;

	tmp = (SORTNODE*)OAL_malloc(sizeof(SORTNODE) * count);
	if (tmp == 0)
	{
		hDB->cb(-888, 0);
		return -1;
	}

	res += load_sort(f_folder, tmp, count);
	if (res == FR_OK)
	{
		_db_sort(tmp, count);
		res += save_sort(f_folder, tmp, count);
	}

	OAL_free(tmp);
	return res;
}
#endif

int enter_folder(char* dir)
{
	int res = 0;
	FOLDERRECORD rec = { 0 };
	const char* f_folder = get_folder_db_name(dir);

	if (f_folder == 0)	return -1;

	assert(hDB->mount == 1);

	if (need_scan(dir, f_folder) == 0)	return 0;

	f_unlink(f_folder);

	res += f_open(&hDB->fdb, f_folder, (unsigned char)(FA_READ | FA_WRITE | FA_CREATE_ALWAYS));

	strcpy(rec.path, dir);
	rec.media_type = MEDIA_HI_FOLDER;

	res += add_record(0, &rec);
	res += scan_file(dir);
	//hDB->dirty &= ~MODULE_FOLDER;
	res += f_close(&hDB->fdb);
#if SORT == 1
	res += sort(f_folder);
#endif
	return res;
}

int get_count_of_folder(char* dir)
{
	int ret = 0;
	int res = 0;
	const char* f_folder = get_folder_db_name(dir);

	if (need_scan(dir, f_folder))
	{
		enter_folder(dir);
	}

	res += f_open(&hDB->fdb, f_folder, (unsigned char)(FA_READ));
	
	if (res)	return -1;

	ret = (int)f_size(&hDB->fdb) / sizeof(FOLDERRECORD) - 1;

	res += f_close(&hDB->fdb);

	return res ? -1 : ret;
}

int get_folder_records(char* dir, unsigned int offset, FOLDERRECORD* records, unsigned int cnt)
{
	unsigned int i = 0;
	int res = 0;
	unsigned int br;
	unsigned int file_count = 0;
	const char* f_folder = get_folder_db_name(dir);

	res += f_open(&hDB->fdb, f_folder, (unsigned char)(FA_READ));
	if (res)	return -1;

	file_count = (int)f_size(&hDB->fdb) / sizeof(FOLDERRECORD) - 1;

	res += f_lseek(&hDB->fdb, sizeof(FOLDERRECORD) * (long long)(offset + 1));
	for (i = 0; i < cnt && i + offset < file_count; i++)
	{
		res += f_read(&hDB->fdb, &records[i], sizeof(FOLDERRECORD), &br);
	}
	res += f_close(&hDB->fdb);
	return res ? -1 : i;
}

int build_fpl(char* dir)
{
	int res = 0;
	unsigned int br;
	FOLDERRECORD rec;

	if (need_scan(dir, F_FPL))
	{
		FIL f;
		const char* f_folder = get_folder_db_name(dir);
		
		f_unlink(F_FPL);

		res += f_open(&f, f_folder, (unsigned char)(FA_READ));
		res += f_open(&hDB->fdb, F_FPL, (unsigned char)(FA_READ | FA_WRITE | FA_CREATE_ALWAYS));
		res += f_read(&f, &rec, sizeof(FOLDERRECORD), &br);
		if (res == FR_OK && br == sizeof(FOLDERRECORD))
		{
			res += f_write(&hDB->fdb, &rec, sizeof(FOLDERRECORD), &br);
			while (res == FR_OK && br == sizeof(FOLDERRECORD))
			{
				res += f_read(&f, &rec, sizeof(FOLDERRECORD), &br);
				if (res == FR_OK && br == sizeof(FOLDERRECORD))
				{
					if (rec.media_type & (MEDIA_HI_AUDIO | MEDIA_HI_WAVE))
					{
						res += f_write(&hDB->fdb, &rec, sizeof(FOLDERRECORD), &br);
					}
				}
			}
		}
		res += f_close(&f);
		res += f_close(&hDB->fdb);
		return res;
	}

	return 0;
}

int get_count_of_fpl(char* dir)
{
	int ret = 0;
	int res = 0;

	if (need_scan(dir, F_FPL))	return -1;

	res += f_open(&hDB->fdb, F_FPL, (unsigned char)(FA_READ));

	if (res)	return -1;

	ret = (int)f_size(&hDB->fdb) / sizeof(FOLDERRECORD) - 1;

	res += f_close(&hDB->fdb);

	return res ? -1 : ret;
}

int get_fpl_records(char* dir, unsigned int offset, FOLDERRECORD* records, unsigned int cnt)
{
	unsigned int i = 0;
	int res = 0;
	unsigned int br;
	unsigned int file_count = 0;

	if (need_scan(dir, F_FPL))	return -1;

	res += f_open(&hDB->fdb, F_FPL, (unsigned char)(FA_READ));
	if (res)	return -1;

	file_count = (int)f_size(&hDB->fdb) / sizeof(FOLDERRECORD) - 1;

	res += f_lseek(&hDB->fdb, sizeof(FOLDERRECORD) * (long long)(offset + 1));
	for (i = 0; i < cnt && i + offset < file_count; i++)
	{
		res += f_read(&hDB->fdb, &records[i], sizeof(FOLDERRECORD), &br);
	}
	res += f_close(&hDB->fdb);
	return res ? -1 : i;
}

int get_fpl_name(char* dir, unsigned int offset, RECUTF8NAME* fname, unsigned int cnt)
{
	unsigned int i = 0;
	int res = 0;
	unsigned int br;
	unsigned int file_count = 0;

	if (need_scan(dir, F_FPL))	return -1;

	res += f_open(&hDB->fdb, F_FPL, (unsigned char)(FA_READ));
	if (res)	return -1;

	file_count = (int)f_size(&hDB->fdb) / sizeof(FOLDERRECORD) - 1;

	res += f_lseek(&hDB->fdb, sizeof(FOLDERRECORD) * (long long)(offset + 1));
	for (i = 0; i < cnt && i + offset < file_count; i++)
	{
		FOLDERRECORD rec = { 0 };
		res += f_read(&hDB->fdb, &rec, sizeof(FOLDERRECORD), &br);
		memcpy(fname[i], rec.fname, MAXUTF8NAME);
	}
	res += f_close(&hDB->fdb);
	return res ? -1 : i;
}

int get_fpl_index(char* path)
{
	unsigned int i = 0;
	int ret = -1;
	int res = 0;
	unsigned int br;
	char dir[MAXPATH];
	unsigned int file_count = 0;

	split_path(path, dir, 0, 0, 0);
	if (need_scan(dir, F_FPL))	return -1;

	res += f_open(&hDB->fdb, F_FPL, (unsigned char)(FA_READ | FA_WRITE));
	if (res)	return -1;

	file_count = (int)f_size(&hDB->fdb) / sizeof(FOLDERRECORD) - 1;
	res += f_lseek(&hDB->fdb, sizeof(FOLDERRECORD));
	for (i = 0; i < file_count; i++)
	{
		FOLDERRECORD rec;
		res += f_read(&hDB->fdb, &rec, sizeof(FOLDERRECORD), &br);
		if (dircmp(path, rec.path) == 0)
		{
			ret = i;
			break;
		}
	}

	res += f_close(&hDB->fdb);
	return res ? -1 : ret;
}

#if SORT == 1

int delete_file_folder_(const char* dir, const char* path)
{
	unsigned int i = 0;
	int res = 0;
	unsigned int br;
	unsigned int file_count = 0;
	FOLDERRECORD rec;
	FIL f;
	const char* f_folder = get_folder_db_name(dir);

	if (need_scan(dir, f_folder))	return 0;

	res += f_open(&hDB->fdb, f_folder, (unsigned char)(FA_READ));
	if (res)	return 0;
	res += f_open(&f, (const char*)(F_SORT), (unsigned char)(FA_READ | FA_WRITE | FA_CREATE_ALWAYS));
	if (res)	return 0;

	file_count = (int)f_size(&hDB->fdb) / sizeof(FOLDERRECORD) - 1;
	res += f_read(&hDB->fdb, &rec, sizeof(FOLDERRECORD), &br);
	res += f_write(&f, &rec, sizeof(FOLDERRECORD), &br);

	for (i = 0; i < file_count; i++)
	{
		res += f_read(&hDB->fdb, &rec, sizeof(FOLDERRECORD), &br);
		if (dircmp(path, rec.path))
		{
			res += f_write(&f, &rec, sizeof(FOLDERRECORD), &br);
		}
		else
		{
			for (++i; i < file_count; i++)
			{
				res += f_read(&hDB->fdb, &rec, sizeof(FOLDERRECORD), &br);
				res += f_write(&f, &rec, sizeof(FOLDERRECORD), &br);
			}
			break;
		}
	}

	res += f_close(&hDB->fdb);
	res += f_close(&f);

	res += f_unlink((const char*)(f_folder));
	res += f_rename((const char*)(F_SORT), (const char*)(f_folder));

	return res;
}

int delete_file_fpl_(const char* dir, const char* path)
{
	unsigned int i = 0;
	int res = 0;
	unsigned int br;
	unsigned int file_count = 0;
	FOLDERRECORD rec;
	FIL f;

	if (need_scan(dir, F_FPL))	return 0;

	res += f_open(&hDB->fdb, (const char*)(F_FPL), (unsigned char)(FA_READ));
	if (res)	return 0;
	res += f_open(&f, (const char*)(F_SORT), (unsigned char)(FA_READ | FA_WRITE | FA_CREATE_ALWAYS));
	if (res)	return 0;

	file_count = (int)f_size(&hDB->fdb) / sizeof(FOLDERRECORD) - 1;
	res += f_read(&hDB->fdb, &rec, sizeof(FOLDERRECORD), &br);
	res += f_write(&f, &rec, sizeof(FOLDERRECORD), &br);

	for (i = 0; i < file_count; i++)
	{
		res += f_read(&hDB->fdb, &rec, sizeof(FOLDERRECORD), &br);
		if (dircmp(path, rec.path))
		{
			res += f_write(&f, &rec, sizeof(FOLDERRECORD), &br);
		}
		else
		{
			for (++i; i < file_count; i++)
			{
				res += f_read(&hDB->fdb, &rec, sizeof(FOLDERRECORD), &br);
				res += f_write(&f, &rec, sizeof(FOLDERRECORD), &br);
			}
			break;
		}
	}

	res += f_close(&hDB->fdb);
	res += f_close(&f);

	res += f_unlink((const char*)(F_FPL));
	res += f_rename((const char*)(F_SORT), (const char*)(F_FPL));

	return res;
}

#else

int delete_file_folder_(const char* dir, const char* path)
{
	unsigned int i = 0;
	int res = 0;
	unsigned int br;
	unsigned int file_count = 0;
	FOLDERRECORD rec;
	const char* f_folder = get_folder_db_name(dir);

	if (need_scan(dir, f_folder))	return 0;

	res += f_open(&hDB->fdb, f_folder, (unsigned char)(FA_READ | FA_WRITE));
	if (res)	return 0;

	file_count = (int)f_size(&hDB->fdb) / sizeof(FOLDERRECORD) - 1;
	res += f_lseek(&hDB->fdb, sizeof(FOLDERRECORD));
	for (i = 0; i < file_count; i++)
	{
		res += f_read(&hDB->fdb, &rec, sizeof(FOLDERRECORD), &br);
		if (dircmp(path, rec.path) == 0)
		{
			long long ofs = f_tell(&hDB->fdb) - sizeof(FOLDERRECORD);
			res += f_lseek(&hDB->fdb, sizeof(FOLDERRECORD) * file_count);
			res += f_read(&hDB->fdb, &rec, sizeof(FOLDERRECORD), &br);
			res += f_lseek(&hDB->fdb, ofs);
			res += f_write(&hDB->fdb, &rec, sizeof(FOLDERRECORD), &br);
			res += f_lseek(&hDB->fdb, sizeof(FOLDERRECORD) * file_count);
			res += f_truncate(&hDB->fdb);
			break;
		}
	}

	res += f_close(&hDB->fdb);
	return res;
}

int delete_file_fpl_(const char* dir, const char* path)
{
	unsigned int i = 0;
	int res = 0;
	unsigned int br;
	unsigned int file_count = 0;
	FOLDERRECORD rec;

	if (need_scan(dir, F_FPL))	return 0;

	res += f_open(&hDB->fdb, (const char*)(F_FPL), (unsigned char)(FA_READ | FA_WRITE));
	if (res)	return 0;

	file_count = (int)f_size(&hDB->fdb) / sizeof(FOLDERRECORD) - 1;
	res += f_lseek(&hDB->fdb, sizeof(FOLDERRECORD));
	for (i = 0; i < file_count; i++)
	{
		res += f_read(&hDB->fdb, &rec, sizeof(FOLDERRECORD), &br);
		if (dircmp(path, rec.path) == 0)
		{
			long long ofs = f_tell(&hDB->fdb) - sizeof(FOLDERRECORD);
			res += f_lseek(&hDB->fdb, sizeof(FOLDERRECORD) * file_count);
			res += f_read(&hDB->fdb, &rec, sizeof(FOLDERRECORD), &br);
			res += f_lseek(&hDB->fdb, ofs);
			res += f_write(&hDB->fdb, &rec, sizeof(FOLDERRECORD), &br);
			res += f_lseek(&hDB->fdb, sizeof(FOLDERRECORD) * file_count);
			res += f_truncate(&hDB->fdb);
			break;
		}
	}

	res += f_close(&hDB->fdb);
	return res;
}

#endif

static int delete_file_(const char* path, int media_type)
{
	char dir[MAXPATH];
	int res = 0;

	split_path(path, dir, 0, 0, 0);
	res += delete_file_folder_(dir, path);
	if (media_type & (MEDIA_HI_AUDIO | MEDIA_HI_WAVE))
	{
		res += delete_file_fpl_(dir, path);
	}

	return res;
}

int delete_file(char* path)
{
	int res = 0;
	int media_type = file_type(path);

	res += f_unlink(path);
	if (res)	return res;

	delete_file_(path, media_type);

	switch(media_type & 0xFFFF0000)
	{
	case MEDIA_HI_AUDIO	:
		res += delete_audio(path);
		res += delete_album(path);
		res += delete_artist(path);
		res += delete_like(path);
		res += delete_tag(path);
		break;

	case MEDIA_HI_VIDEO	:
		res += delete_video(path);
		break;

	case MEDIA_HI_IMAGE	:
		res += delete_image(path);
		break;

	case MEDIA_HI_EBOOK	:
		res += delete_ebook(path);
		res += delete_tag(path);
		break;

	case MEDIA_HI_WAVE	:
		res += delete_wave(path);
		res += delete_like(path);
		res += delete_tag(path);
		break;

	case MEDIA_HI_FOLDER:
	default:
		assert(0);
		break;
	}

	return res;
}

extern int get_audio_info(AUDIORECORD* rec, int media_type);
int get_audio_id3(char* path, AUDIORECORD* record)
{
	int res = 0;
	int media_type = file_type(path);
	if ((media_type & MEDIA_HI_AUDIO) == 0)
		return -1;

	strcpy(record->path, path);
	res += get_audio_info(record, media_type);
	if (res)
		return -1;
	
	if (record->album[0] == 0)
		strcpy(record->album, "unknow");
	if (record->artist[0] == 0)
		strcpy(record->artist, "unknow");

	return 0;
}
